/*
 * Matrix4D.java
 *
 * Created on 5 de Dezembro de 2007, 15:21
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package mathematics;

/**
 *
 * @author rafaelbuck
 */
public final class Matrix4D
{

    /** Creates a new instance of Matrix4D */
    public Matrix4D()
    {
        xx = yy = zz = ww = 1.0F;
    }

    /** Creates a new instance of Matrix4D */
    public Matrix4D(Matrix4D m)
    {
        xx = m.xx;
        xy = m.xy;
        xz = m.xz;
        xw = m.xw;
        yx = m.yx;
        yy = m.yy;
        yz = m.yz;
        yw = m.yw;
        zx = m.zx;
        zy = m.zy;
        zz = m.zz;
        zw = m.zw;
        wx = m.wx;
        wy = m.wy;
        wz = m.wz;
        ww = m.ww;
    }

    /** Creates a new instance of Matrix4D */
    public Matrix4D(float mxx, float mxy, float mxz, float mxw, float myx, float myy, float myz, 
            float myw, float mzx, float mzy, float mzz, float mzw, float mwx, float mwy, 
            float mwz, float mww)
    {
        xx = mxx;
        xy = mxy;
        xz = mxz;
        xw = mxw;
        yx = myx;
        yy = myy;
        yz = myz;
        yw = myw;
        zx = mzx;
        zy = mzy;
        zz = mzz;
        zw = mzw;
        wx = mwx;
        wy = mwy;
        wz = mwz;
        ww = mww;
    }

    public void identity()
    {
        xx = yy = zz = ww = 1.0F;
        xy = xz = xw = yx = yz = yw = zx = zy = zw = wx = wy = wz = 0.0F;
    }

    public void transponize()
    {
        float t = xy;
        xy = yx;
        yx = t;
        t = xz;
        xz = zx;
        zx = t;
        t = xw;
        xw = wx;
        wx = t;
        t = yz;
        yz = zy;
        zy = t;
        t = yw;
        yw = wy;
        wy = t;
        t = zw;
        zw = wz;
        wz = t;
    }

    public Vector3D mult(Vector3D v)
    {
        return new Vector3D(xx * v.x + xy * v.y + xz * v.z, yx * v.x + yy * v.y + yz * v.z, zx * v.x + zy * v.y + zz * v.z);
    }

    public void transform(Vector3D v)
    {
        float x = xx * v.x + xy * v.y + xz * v.z;
        float y = yx * v.x + yy * v.y + yz * v.z;
        float z = zx * v.x + zy * v.y + zz * v.z;
        v.x = x;
        v.y = y;
        v.z = z;
    }

    public Point3D mult(Point3D p)
    {
        Point3D ret = new Point3D(xx * p.x + xy * p.y + xz * p.z + xw, yx * p.x + yy * p.y + yz * p.z + yw, zx * p.x + zy * p.y + zz * p.z + zw);
        ret.scale(1.0F / (wx * p.x + wy * p.y + wz * p.z + ww));
        return ret;
    }

    public void transform(Point3D p)
    {
        float x = xx * p.x + xy * p.y + xz * p.z + xw;
        float y = yx * p.x + yy * p.y + yz * p.z + yw;
        float z = zx * p.x + zy * p.y + zz * p.z + zw;
        float w = wx * p.x + wy * p.y + wz * p.z + ww;
        p.x = x / w;
        p.y = y / w;
        p.z = z / w;
    }

    public Matrix4D mult(Matrix4D m)
    {
        if(m != null)
            return new Matrix4D(xx * m.xx + xy * m.yx + xz * m.zx + xw * m.wx, xx * m.xy + xy * m.yy + xz * m.zy + xw * m.wy, xx * m.xz + xy * m.yz + xz * m.zz + xw * m.wz, xx * m.xw + xy * m.yw + xz * m.zw + xw * m.ww, yx * m.xx + yy * m.yx + yz * m.zx + yw * m.wx, yx * m.xy + yy * m.yy + yz * m.zy + yw * m.wy, yx * m.xz + yy * m.yz + yz * m.zz + yw * m.wz, yx * m.xw + yy * m.yw + yz * m.zw + yw * m.ww, zx * m.xx + zy * m.yx + zz * m.zx + zw * m.wx, zx * m.xy + zy * m.yy + zz * m.zy + zw * m.wy, zx * m.xz + zy * m.yz + zz * m.zz + zw * m.wz, zx * m.xw + zy * m.yw + zz * m.zw + zw * m.ww, wx * m.xx + wy * m.yx + wz * m.zx + ww * m.wx, wx * m.xy + wy * m.yy + wz * m.zy + ww * m.wy, wx * m.xz + wy * m.yz + wz * m.zz + ww * m.wz, wx * m.xw + wy * m.yw + wz * m.zw + ww * m.ww);
        else
            return new Matrix4D(this);
    }

    public void multBy(Matrix4D m)
    {
        float x = xx * m.xx + xy * m.yx + xz * m.zx + xw * m.wx;
        float y = xx * m.xy + xy * m.yy + xz * m.zy + xw * m.wy;
        float z = xx * m.xz + xy * m.yz + xz * m.zz + xw * m.wz;
        float w = xx * m.xw + xy * m.yw + xz * m.zw + xw * m.ww;
        xx = x;
        xy = y;
        xz = z;
        xw = w;
        x = yx * m.xx + yy * m.yx + yz * m.zx + yw * m.wx;
        y = yx * m.xy + yy * m.yy + yz * m.zy + yw * m.wy;
        z = yx * m.xz + yy * m.yz + yz * m.zz + yw * m.wz;
        w = yx * m.xw + yy * m.yw + yz * m.zw + yw * m.ww;
        yx = x;
        yy = y;
        yz = z;
        yw = w;
        x = zx * m.xx + zy * m.yx + zz * m.zx + zw * m.wx;
        y = zx * m.xy + zy * m.yy + zz * m.zy + zw * m.wy;
        z = zx * m.xz + zy * m.yz + zz * m.zz + zw * m.wz;
        w = zx * m.xw + zy * m.yw + zz * m.zw + zw * m.ww;
        zx = x;
        zy = y;
        zz = z;
        zw = w;
        x = wx * m.xx + wy * m.yx + wz * m.zx + ww * m.wx;
        y = wx * m.xy + wy * m.yy + wz * m.zy + ww * m.wy;
        z = wx * m.xz + wy * m.yz + wz * m.zz + ww * m.wz;
        w = wx * m.xw + wy * m.yw + wz * m.zw + ww * m.ww;
        wx = x;
        wy = y;
        wz = z;
        ww = w;
    }

    public void multLeftBy(Matrix4D m)
    {
        float x = m.xx * xx + m.xy * yx + m.xz * zx + m.xw * wx;
        float y = m.yx * xx + m.yy * yx + m.yz * zx + m.yw * wx;
        float z = m.zx * xx + m.zy * yx + m.zz * zx + m.zw * wx;
        float w = m.wx * xx + m.wy * yx + m.wz * zx + m.ww * wx;
        xx = x;
        yx = y;
        zx = z;
        wx = w;
        x = m.xx * xy + m.xy * yy + m.xz * zy + m.xw * wy;
        y = m.yx * xy + m.yy * yy + m.yz * zy + m.yw * wy;
        z = m.zx * xy + m.zy * yy + m.zz * zy + m.zw * wy;
        w = m.wx * xy + m.wy * yy + m.wz * zy + m.ww * wy;
        xy = x;
        yy = y;
        zy = z;
        wy = w;
        x = m.xx * xz + m.xy * yz + m.xz * zz + m.xw * wz;
        y = m.yx * xz + m.yy * yz + m.yz * zz + m.yw * wz;
        z = m.zx * xz + m.zy * yz + m.zz * zz + m.zw * wz;
        w = m.wx * xz + m.wy * yz + m.wz * zz + m.ww * wz;
        xz = x;
        yz = y;
        zz = z;
        wz = w;
        x = m.xx * xw + m.xy * yw + m.xz * zw + m.xw * ww;
        y = m.yx * xw + m.yy * yw + m.yz * zw + m.yw * ww;
        z = m.zx * xw + m.zy * yw + m.zz * zw + m.zw * ww;
        w = m.wx * xw + m.wy * yw + m.wz * zw + m.ww * ww;
        xw = x;
        yw = y;
        zw = z;
        ww = w;
    }

    public void translate(float x, float y, float z)
    {
        xw += x * xx + y * xy + z * xz;
        yw += x * yx + y * yy + z * yz;
        zw += x * zx + y * zy + z * zz;
        ww += x * wx + y * wy + z * wz;
    }

    public void translate(Vector3D v)
    {
        translate(v.x, v.y, v.z);
    }

    public void translateLeft(float x, float y, float z)
    {
        if(x != 0.0F)
        {
            xx += x * wx;
            xy += x * wy;
            xz += x * wz;
            xw += x * ww;
        }
        if(y != 0.0F)
        {
            yx += y * wx;
            yy += y * wy;
            yz += y * wz;
            yw += y * ww;
        }
        if(z != 0.0F)
        {
            zx += z * wx;
            zy += z * wy;
            zz += z * wz;
            zw += z * ww;
        }
    }

    public void translateLeft(Vector3D v)
    {
        translateLeft(v.x, v.y, v.z);
    }

    public void moveBy(float x, float y, float z)
    {
        if(x != 0.0F)
        {
            xx += x * xw;
            yx += x * yw;
            zx += x * zw;
            wx += x * ww;
        }
        if(y != 0.0F)
        {
            xy += y * xw;
            yy += y * yw;
            zy += y * zw;
            wy += y * ww;
        }
        if(z != 0.0F)
        {
            xz += z * xw;
            yz += z * yw;
            zz += z * zw;
            wz += z * ww;
        }
    }

    public void moveBy(Vector3D v)
    {
        moveBy(v.x, v.y, v.z);
    }

    public void yrot(double theta)
    {
        if(theta == 0.0D)
        {
            return;
        } else
        {
            double ct = Math.cos(theta);
            double st = -Math.sin(theta);
            float Nx = (float)((double)xx * ct - (double)zx * st);
            float Nz = (float)((double)xx * st + (double)zx * ct);
            xx = Nx;
            zx = Nz;
            Nx = (float)((double)xy * ct - (double)zy * st);
            Nz = (float)((double)xy * st + (double)zy * ct);
            xy = Nx;
            zy = Nz;
            Nx = (float)((double)xz * ct - (double)zz * st);
            Nz = (float)((double)xz * st + (double)zz * ct);
            xz = Nx;
            zz = Nz;
            Nx = (float)((double)xw * ct - (double)zw * st);
            Nz = (float)((double)xw * st + (double)zw * ct);
            xw = Nx;
            zw = Nz;
            return;
        }
    }

    public void xrot(double theta)
    {
        if(theta == 0.0D)
        {
            return;
        } else
        {
            double ct = Math.cos(theta);
            double st = Math.sin(theta);
            float Ny = (float)((double)yx * ct - (double)zx * st);
            float Nz = (float)((double)yx * st + (double)zx * ct);
            yx = Ny;
            zx = Nz;
            Ny = (float)((double)yy * ct - (double)zy * st);
            Nz = (float)((double)yy * st + (double)zy * ct);
            yy = Ny;
            zy = Nz;
            Ny = (float)((double)yz * ct - (double)zz * st);
            Nz = (float)((double)yz * st + (double)zz * ct);
            yz = Ny;
            zz = Nz;
            Ny = (float)((double)yw * ct - (double)zw * st);
            Nz = (float)((double)yw * st + (double)zw * ct);
            yw = Ny;
            zw = Nz;
            return;
        }
    }

    public void zrot(double theta)
    {
        if(theta == 0.0D)
        {
            return;
        } else
        {
            double ct = Math.cos(theta);
            double st = Math.sin(theta);
            float Nx = (float)((double)xx * ct - (double)yx * st);
            float Ny = (float)((double)xx * st + (double)yx * ct);
            xx = Nx;
            yx = Ny;
            Nx = (float)((double)xy * ct - (double)yy * st);
            Ny = (float)((double)xy * st + (double)yy * ct);
            xy = Nx;
            yy = Ny;
            Nx = (float)((double)xz * ct - (double)yz * st);
            Ny = (float)((double)xz * st + (double)yz * ct);
            xz = Nx;
            yz = Ny;
            Nx = (float)((double)xw * ct - (double)yw * st);
            Ny = (float)((double)xw * st + (double)yw * ct);
            xw = Nx;
            yw = Ny;
            return;
        }
    }

    public void scale(float f)
    {
        xx *= f;
        yx *= f;
        zx *= f;
        wx *= f;
        xy *= f;
        yy *= f;
        zy *= f;
        wy *= f;
        xz *= f;
        yz *= f;
        zz *= f;
        wz *= f;
    }

    public void scale(float fx, float fy, float fz)
    {
        xx *= fx;
        yx *= fx;
        zx *= fx;
        wx *= fx;
        xy *= fy;
        yy *= fy;
        zy *= fy;
        wy *= fy;
        xz *= fz;
        yz *= fz;
        zz *= fz;
        wz *= fz;
    }

    public void transform(Point3D v[], int tx[], int ty[], int tz[], int npoints)
    {
        for(int i = npoints; --i >= 0;)
        {
            Point3D tmp = mult(v[i]);
            tx[i] = (int)tmp.x;
            ty[i] = (int)tmp.y;
            tz[i] = (int)tmp.z;
        }

    }

    public String toString()
    {
        return new String((new StringBuffer()).append("{{").append(xx).append(",").append(xy).append(",").append(xz).append(",").append(xw).append("},{").append(yx).append(",").append(yy).append(",").append(yz).append(",").append(yw).append("},{").append(zx).append(",").append(zy).append(",").append(zz).append(",").append(zw).append("},{").append(wx).append(",").append(wy).append(",").append(wz).append(",").append(ww).append("}}"));
    }

    private static final double DEG2RAD = 0.017453292519943295D;
    public float xx;
    public float xy;
    public float xz;
    public float xw;
    public float yx;
    public float yy;
    public float yz;
    public float yw;
    public float zx;
    public float zy;
    public float zz;
    public float zw;
    public float wx;
    public float wy;
    public float wz;
    public float ww;
}